<?php
declare (strict_types = 1);
namespace app\common\model\user;

use app\admin\traits\GetTableData;
use think\Exception;
use think\Model;

/**
 * @mixin think\Model
 */
class Permission extends Model
{
    use GetTableData;

    const TYPE_PERMISSION = 2;
    const TYPE_ROLE = 1;

    //规则说明
    const RULE_ROLE_DISABLED = 1;  //当前角色启用规则
    const RULE_ROLE_ENABLED = 2;  //当前角色不启用规则

    const RULE_PERMISSION_ALL = 1; //正常权限规则(可操作所有内容)
    const RULE_PERMISSION_OWNER = 2;//所属权限规则(仅可操作自己的内容)


    // 设置字段信息
    protected $schema = [
        'name'              => 'string',
        'type'              => 'int',
        'rule'              => 'string',
        'desc'              => 'string',
        'create_time'       => 'int',
        'update_time'       => 'int',
    ];

    // 类型转换
    protected $type = [
        'type'              => 'integer',
    ];

    //删除之后删除关联数据(有外键用不到)
    public static function onAfterDelete(Model $model)
    {
        //删除关联数据
        $xid = $model->type == static::TYPE_ROLE ? 'role_id' : 'permission_id';
        RolePermission::where($xid,'=',$model->id)->delete();
    }

    //定义关联
    public function roles()
    {
        if ($this->getAttr('type') === static::TYPE_ROLE) return [];
        return $this->belongsToMany(static::class, RolePermission::class,'role_id','permission_id');
    }
    public function permissions()
    {
        if ($this->getAttr('type') === static::TYPE_PERMISSION) return [];
        return $this->belongsToMany(static::class, RolePermission::class,'permission_id','role_id');
    }

    //更新授权
    public function updateRelate(array $permission_ids){
        //先删除该角色的所有授权
        RolePermission::destroy(function($query){
            $query->where('role_id','=',$this->id);
        });

        //再添加授权信息
        if (!empty($permission_ids))
            $this->permissions()->saveAll($permission_ids);
        return true;
    }

    //保存路由信息
    public static function saveRoute($data){
        if (!is_array($data)) return false;


        $added = static::getAdded();

        $will_add = array_diff($data, $added);
        $will_del = array_diff($added, $data);

        //先删除 后添加
        if ( !empty($will_del) ){
            static::destroy(array_keys($will_del));
        }

        //添加
        if (!empty($will_add)){
            return (new static())->saveAll(static::formatArr($will_add));
        }

        return true;


    }

    /**
     * #构建批量写入数据
     * @param $arr
     * @return array
     */
    public static function formatArr($arr){
        if (!is_array($arr) || empty($arr)) return [];

        $ret = [];
        foreach ($arr as $k => $v){
            $ret[] = [
                'name' => $v,
                'type' => static::TYPE_PERMISSION,
                'rule' => static::RULE_PERMISSION_OWNER,
                'desc' => $v
            ];
        }
        return $ret;
    }

    //获取指定角色已经关联的路由id
    public static function getRelated(int $id){
        return RolePermission::where(['role_id'=>$id])->column('permission_id','id');
    }

    //获取已经添加的路由
    public static function getAdded(){
        return static::where(['type'=>static::TYPE_PERMISSION])->column('name','id');
    }

    //获取分类后的路由列表数据
    public static function getRouteList(){
        $data = static::getAdded();
        //分隔符
        $ds = config('route.pathinfo_depr');
        $res = [];
        foreach ($data as $id => $val){
            $k = strchr($val, $ds, true);
            if (empty($k)) continue;
            $res[$k][$id] = $val;
        }
        return $res;
    }

    //获取未添加的路由
    public static function getNotAdded(){
        //分隔符
        $ds = config('route.pathinfo_depr');

        $path = app()->getBasePath(). 'admin'. DS . 'controller';
        $all =  array_merge(['*'. $ds .'*'], static::getRoutes($path));
        $added = static::getAdded();
        return array_diff($all, $added);
    }

    /**
     * #递归获取所有路由信息
     * @param $path
     * @return array
     * @throws Exception
     */
    public static function getRoutes($path){
        if (!is_dir($path)) throw new Exception('参数错误.');
        //分隔符
        $ds = config('route.pathinfo_depr');

        $path = rtrim($path, DS) . DS;
        $routes = [];

        $files = glob($path . '*');
        foreach ($files as $file){
            if (is_dir($file)){
                $routes = array_merge(self::getRoutes($file), $routes);
                continue;
            }
            $content = file_get_contents($file);
            preg_match('/class ([a-zA-Z]+) extends/', $content, $match);
            $cName = $match[1];
            $routes[] = parse_name($cName . $ds . '*');

            preg_match_all('/public function ([a-zA-Z_]+)/', $content, $matchs);
            if (!empty($matchs[1])){
                foreach ($matchs[1] as $match) {
                    $routes[] = parse_name($cName . $ds . $match);
                }
            }
        }
        return $routes;
    }

    //获取列表数据
    public static function getListData(array $ruleWhere=[]){
        //要查询的字段
        $fields = [
            'fields' =>['id','name','type','rule','desc','create_time','update_time'],
            'with' => [],
            'withCount' => [],
        ];

        //(构建查询条件)
        $where = [];
        if (input('start_time')){
            $where[] = ['create_time', '>=', strtotime(input('start_time'))];
        }
        if (input('end_time')){
            $where[] = ['create_time', '<=', strtotime( input('end_time'))];
        }
        if (input('type/d')){
            $where[] = ['type','=',input('type/d')];
        }

        //排序
        $order = ['id'=>'desc','create_time'=>'desc'];

        //[count:查询到的数目, list:记录列表]
        return static::operateDatum($where, $fields, $order,$ruleWhere);
    }

    /***
     * 获取所有的角色
     */
    public static function getAllRoles(){
        return static::where(['type'=>static::TYPE_ROLE])->column('name','id');
    }
    /**
     * 获取指定用户拥有的角色
     */
    public static function getRelatedRoles(int $id){
        return Assignment::where(['user_id'=>$id])->column('permission_id','id');
    }

    /**
     * 获取指定用户是否使用权限规则(不管用户有多少个角色,其中任何一个橘色使用规则那导致当前权限使用规则)
     * @param array $ids 用户拥有的角色id数组
     * @return int
     */
    public static function getRelatedRule(array $ids){
        return static::where([
            ['rule', '=', static::RULE_ROLE_ENABLED],
            ['id', 'in', $ids]
        ])->count();
    }

    /**
     * 获取指定用户拥有的权限路由
     * @param int $id 用户id
     * @return array 返回用户拥有的权限和是否使用规则
     */
    public static function getRelatedPermissions(int $id){

        //用户关联的角色
        $ids = static::getRelatedRoles($id);

        $permission = static::where([
            'type' => static::TYPE_PERMISSION,
        ])->where(
            'id',
            'in',
            RolePermission::where(
                'role_id',
                'in',
                $ids
            )->column('permission_id')
        )->column('rule','name');

        $rule = static::getRelatedRule($ids);

        return [
            'permission' => $permission,
            'rule' => $rule
        ];
    }


}
